iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

iOS Junior的菜雞之路系列 第 9

可不可以把影片放進去一下下就好

  • 分享至 

  • xImage
  •  

我想要在我的頁面中放進去一個影片預告片的內容
這次會用到Appple的原生Module: AVFoundation
專門在處理影片相關的內容

組建介紹

  • AVAsset: 負責多媒體資料
  • AVPlayerItem:提供播放數據來源,管理媒體資源
  • AVPlayer: 負責播放器的播放暫停功能
  • AVPlayerLayer:負責顯示影片

建立播放的View

先創立一個View來作為播放的View

import AVFoundation
import UIKit

class VideoView: UIView {
    private var playerItemContext = 0
    
    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }
    
    var player: AVPlayer? {
        get {
            return playerLayer.player
        } set {
            return playerLayer.player = newValue
        }
    }
    
    private func initPlayerAsset(with url: URL, completion: ((_ assets: AVAsset) -> Void)?) {
        let assets = AVAsset(url: url)
        assets.loadValuesAsynchronously(forKeys: ["playable"]) {
            completion?(asset)
        }
    }
    
    func play(with url: URL) {
        initPlayerAsset(with: url) { (asset: AVAsset) in
            let item = AVPlayerItem(asset: asset)
            // KVO,註冊通知                                   
            item.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), option: [.old, .new], context: &self.playerItemContext)
            DispatchQueue.main.async {
                self.player = AVPlayer(playerItem: item)
            }                                    
        }
    }
    
    // Observe接收到通知後,就會執行這段
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer? ) {
        guard context != &self.playerItemContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return 
        }
        
        if keyPath == #keyPath(AVPlayerItem.status) {
            let status: AVPlayerItem.Status
            if let statusNumber = change?[.newKey] as? NSNumber {
                status = AVPlayerItem.Status(rawValue: statusNumber.initValue)!
            } else {
                status = .unknown    
            }
            
            // 用來判斷目前的狀態
            switch status {
            case .readyToPlay:
                self.player?.play()
                print("Ready to play")
            case .failed:
                print("Failed")
            case .unknown:
                print("Unknow")
            @unknown default:
                print("Unknow Failed")
            }
        }   
    }
}

因為我要放在tableViewCell內部所以我拉一個View在tableViewCell內部,將該View CustomClass改成VideoView

再加入一個Button按鈕作為開始/暫停的控制按鈕
記得將初始化的Code也一併寫入

//tableViewCell.swift


@IBOutlet weak var videoView: VideoView!
@IBOutlet weak var controlButton: UIButton! {
    didSet {
        controlButton.addTarget(self, action: #selector(switchControl), for: .touchUpInside)
    }
}

// 開始/暫停開關Func
@objc func switchControl() {
    if controlButton.titleLabel?.text == "Pause" {
        video.player?.pause()
        controlButton.setTitle("Start", for: .normal)
    } else {
        video.player?.start()
        controlButton.setTitle("Pause", for: .normal)
    }
}

.
.
.

func configure(url: URL) {
    // 影片初始化流程(含開始播放)
    videoView.play(with: url)
}

這樣就可以完美的把影片播出來,只是其他細節設定都還沒處理

參考資料:
https://medium.com/@tarasuzy00/build-video-player-in-ios-i-avplayer-43cd1060dbdc
https://www.jianshu.com/p/20e5e6d5f3e5


坑:
原本看到override class var layerClass的時候有點不明所以,所以忽略不寫
但是後來發現要先將自身的layer轉型成AVPlayerLayer才能夠使用


上一篇
風起雲湧,資料薈萃的API串接
下一篇
快捷點按,迅速切換 TabBar
系列文
iOS Junior的菜雞之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言